package com.xxl.job.executor.protocol;


import com.xxl.job.executor.constant.FireConstant;
import com.xxl.job.executor.utils.ByteUtil;
import com.xxl.job.executor.utils.DateUtil;
import com.xxl.job.executor.utils.TransUtils;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

public class TCPProtocolCrc {


    private String inetHost = null;
    private int inetPort;
    private Socket socket;
    ServerSocket serverSocket;

    public TCPProtocolCrc() {

    }


    /**
     * 计算CRC16校验码
     *
     * @param data 需要校验的字符串
     * @return 校验码
     */
    public static String getCRC(String data) {
        data = data.replace(" ", "");
        int len = data.length();
        if (!(len % 2 == 0)) {
            return "0000";
        }
        int num = len / 2;
        byte[] para = new byte[num];
        for (int i = 0; i < num; i++) {
            int value = Integer.valueOf(data.substring(i * 2, 2 * (i + 1)), 16);
            para[i] = (byte) value;
        }
        return getCRC(para);
    }


    /**
     * 计算CRC16校验码
     *
     * @param bytes 字节数组
     * @return {@link String} 校验码
     * @since 1.0
     */
    public static String getCRC(byte[] bytes) {
        //CRC寄存器全为1
        int CRC = 0x0000ffff;
        //多项式校验值
        int POLYNOMIAL = 0x0000a001;
        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        //结果转换为16进制
        String result = Integer.toHexString(CRC).toUpperCase();
        if (result.length() != 4) {
            StringBuffer sb = new StringBuffer("0000");
            result = sb.replace(4 - result.length(), 4, result).toString();
        }
        //交换高低位
        return result.substring(2, 4) + result.substring(0, 2);
    }


    public static String ascii2crc16_2(String msg){
        StringBuilder stringBuilder=new StringBuilder();
        String[] msgArr=msg.split("<ETB>");
        String crcData=msgArr[0];

        char[] crcChar=crcData.toCharArray();
        for(int i=0;i<crcChar.length;i++){
            stringBuilder.append(Integer.toHexString((int)crcChar[i]));
        }

        stringBuilder.append("17");

        return stringBuilder.toString();
    }

    // ASCII码转CRC16编码
    public static String ascii2crc16(String msg){
        StringBuilder stringBuilder=new StringBuilder();

        // 切割字符串
        String[] msgArr=msg.replace("<STX>","<H>").replace("<ETB>","<H>").replace("<ETX>","<H>").split("<H>");

        String head=msgArr[0];
        String body=msgArr[1];

        if(msg.contains("<ETB>")){
            String crc=msgArr[2];
        }

        // 处理头信息
        if("".equals(head)){
            stringBuilder.append("02"); //STX
        }else{
            // 将字符转换为16进制数
            char[] data=head.toCharArray();
            for(int i=0;i<data.length;i++){
                stringBuilder.append(Integer.toHexString((int)data[i]));
            }
            stringBuilder.append("02"); //STX
        }

        // 处理消息体
        char[] bodyData=body.toCharArray();
        for(int i=0;i<bodyData.length;i++){
            stringBuilder.append(Integer.toHexString((int)bodyData[i]));
        }

        // 处理crc码
        if(msg.contains("<ETB>")){
            stringBuilder.append("17"); //ETB
            String crc=msgArr[2];
            char[] crcData=crc.toCharArray();
            for(int i=0;i<crcData.length;i++){
                stringBuilder.append(Integer.toHexString((int)crcData[i]));
            }
        }

        stringBuilder.append("03"); //ETX

        return stringBuilder.toString().toUpperCase();
    }

    public static void main(String[] args) {
        String hexChar="023032524D543D35363738174246303803";
        String[] charArr=new String[hexChar.length()/2];

        int j2=0;
        for(int i=0;i<hexChar.length();i++){
            if(i%2==0){//每隔bai两个
                charArr[j2]=""+hexChar.charAt(i);
            }else{
                charArr[j2]=charArr[j2]+""+hexChar.charAt(i);//将字符加上两个空格du
                j2++;
            }
        }

        System.out.println(ByteUtil.hexChars2bytes(hexChar.toCharArray()));

        String msg="49CLOCK<ETB>";
        String crcCode=getCRC(ascii2crc16_2(msg));
        String crcCodeLast=crcCode.substring(2,4)+crcCode.substring(0,2);
        System.out.println(crcCodeLast);
        System.out.println(ascii2crc16("<STX>"+msg+""+crcCodeLast+"<ETX>"));

        String acciiStr="02 39 33 50 53 54 54 31 3D 2D 2D 2D 2D 2D 2D 2D 2D 52 2D 45 2D 2D 2D 2D 2D 17 31 33 31 46 03";
        acciiStr=acciiStr.replace(" ","");
        String[] strArr=new String[acciiStr.length()/2];
        byte[] byteArr=new byte[acciiStr.length()/2];

        int j=0;
        for(int i=0;i<acciiStr.length();i++){
            if(i%2==0){//每隔bai两个
                strArr[j]=""+acciiStr.charAt(i);
            }else{
                strArr[j]=strArr[j]+""+acciiStr.charAt(i);//将字符加上两个空格du
                j++;
            }
        }

        StringBuilder stringBuilder=new StringBuilder();
        for(int i=0;i<strArr.length;i++){
            byte b = (byte) (0xff & Integer.parseInt(strArr[i], 16));
            // <STX>
            if(b==0x02){
                stringBuilder.append("<STX>");
            }
            // <ETB>
            else if(b==0x17){
                stringBuilder.append("<ETB>");
            }
            // <ETX>
            else if(b==0x03){
                stringBuilder.append("<ETX>");
            }else{
                stringBuilder.append((char)b);
            }
        }

        System.out.println(stringBuilder.toString());
    }


    /**
     * 字符串转换为16进制byte数组
     * @param value
     * @return
     */
    public static byte[] stringTo16ByteArr(String value){
        String acciiStr=ascii2crc16(value);
        acciiStr=acciiStr.replace(" ","");
        String[] strArr=new String[acciiStr.length()/2];
        byte[] byteArr=new byte[acciiStr.length()/2];

        int j=0;
        for(int i=0;i<acciiStr.length();i++){
            if(i%2==0){//每隔bai两个
                strArr[j]=""+acciiStr.charAt(i);
            }else{
                strArr[j]=strArr[j]+""+acciiStr.charAt(i);//将字符加上两个空格du
                j++;
            }
        }

        StringBuilder stringBuilder=new StringBuilder();
        for(int i=0;i<strArr.length;i++){
            byte b = (byte) (0xff & Integer.parseInt(strArr[i], 16));
            // <STX>
            if(b==0x02){
                stringBuilder.append("<STX>");
            }
            // <ETB>
            else if(b==0x17){
                stringBuilder.append("<ETB>");
            }
            // <ETX>
            else if(b==0x03){
                stringBuilder.append("<ETX>");
            }else{
                stringBuilder.append((char)b);
            }
            byteArr[i]=b;
        }

        return byteArr;
    }

    /**
     * 字符串转换为Ascii
     * @param value
     * @return
     */
    public static String stringToAscii(String value)
    {
        StringBuffer sbu = new StringBuffer();
        char[] chars = value.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if(i != chars.length - 1)
            {
                sbu.append((int)chars[i]).append(",");
            }
            else {
                sbu.append((int)chars[i]);
            }
        }
        return sbu.toString();
    }


    public TCPProtocolCrc(String inetHost, int inetPort) {
        this.inetHost = inetHost;
        this.inetPort = inetPort;
    }

    public boolean sever() throws Exception{
        // 1.创建服务器ServerSocket对象和系统指定的端口号
        serverSocket = new ServerSocket(55000);
        while (true) {
//            try {
                Socket socket = serverSocket.accept();
                System.out.println("创建 <" + inetHost + " TCPProtocol> 成功，等待接收数据 ");
                System.out.println("------------------------------------");
                System.out.println("ClientIp:" + socket.getInetAddress());
                System.out.println("ClientPort:" + socket.getPort());
                System.out.println("------------------------------------");
                new Thread(new ServerThread(socket)).start(); //为每个客户端连接开启一个线程
//            } catch (IOException e) {
//                System.out.println("创建 <" + inetHost + " TCPProtocol> 失败，等待接收数据 ");
//            }
        }
    }

    public boolean connect() {
        try {
            socket = new Socket();
            SocketAddress socketAddress = new InetSocketAddress(inetHost, inetPort);
            socket.connect(socketAddress, 3000);
            socket.setSoTimeout(3000);
            return true;
        } catch (IOException e) {
            System.out.println("TCPProtocol连接 <" + inetHost + ":" + inetPort + "> 失败!!!  " + e.getMessage());
            return false;
        }
    }

    public boolean sendData(byte[] sMessage) {
        try {
            OutputStream os = socket.getOutputStream();
            os.write(sMessage);
            os.flush();
            return true;
        } catch (IOException e) {
            System.out.println("TCPProtocol Send Mesaage Error!!!" + e.getMessage());
            return false;
        }
    }

    public static String ascii2str(String acciiStr){
        String[] strArr=new String[acciiStr.length()/2];
        byte[] byteArr=new byte[acciiStr.length()/2];

        int j=0;
        for(int i=0;i<acciiStr.length();i++){
            if(i%2==0){//每隔bai两个
                strArr[j]=""+acciiStr.charAt(i);
            }else{
                strArr[j]=strArr[j]+""+acciiStr.charAt(i);//将字符加上两个空格du
                j++;
            }
        }

        StringBuilder stringBuilder=new StringBuilder();
        for(int i=0;i<strArr.length;i++){
            byte b = (byte) (0xff & Integer.parseInt(strArr[i], 16));
            // <STX>
            if(b==0x02){
                stringBuilder.append("<STX>");
            }
            // <ETB>
            else if(b==0x17){
                stringBuilder.append("<ETB>");
            }
            // <ETX>
            else if(b==0x03){
                stringBuilder.append("<ETX>");
            }else{
                stringBuilder.append((char)b);
            }
        }

        return stringBuilder.toString();
    }


    // ASCII码转CRC16编码
    public static String str2ascii(String msg){
        StringBuilder stringBuilder=new StringBuilder();

        // 切割字符串
        String[] msgArr=msg.replace("<STX>","<H>").replace("<ETB>","<H>").replace("<ETX>","<H>").split("<H>");

        String head=msgArr[0];
        String body=msgArr[1];
        String crc=msgArr[2];

        // 处理头信息
        if("".equals(head)){
            stringBuilder.append("02"); //STX
        }else{
            // 将字符转换为16进制数
            char[] data=head.toCharArray();
            for(int i=0;i<data.length;i++){
                stringBuilder.append(Integer.toHexString((int)data[i]));
            }
            stringBuilder.append("02"); //STX
        }

        // 处理消息体
        char[] bodyData=body.toCharArray();
        for(int i=0;i<bodyData.length;i++){
            stringBuilder.append(Integer.toHexString((int)bodyData[i]));
        }
        stringBuilder.append("17"); //ETB

        // 处理crc码
        char[] crcData=crc.toCharArray();
        for(int i=0;i<crcData.length;i++){
            stringBuilder.append(Integer.toHexString((int)crcData[i]));
        }
        stringBuilder.append("03"); //ETX

        return stringBuilder.toString().toUpperCase();
    }

    public byte[] recvData() {
        String recvMessage = "";
        int iMaxLenth = 2048;
        int iRecvMessageLen = 0;
        byte[] bString = new byte[iMaxLenth];
        try {
            InputStream is = socket.getInputStream();
            DataInputStream input = new DataInputStream(is);

            // while (true) {
            try {
                int i = input.read(bString);
                iRecvMessageLen += i;
                recvMessage += ByteUtil.bytes2hexStr(bString);
//                    TimeUnit.SECONDS.sleep(recvSleep);
            } catch (IOException e) {
//                System.out.println("Recv Message End");
                //    break;
            }
            //   }
            if (!StringUtils.isNotBlank(recvMessage)) {
//                System.out.println("Recv message is null");
                return null;
            }
            byte[] bTemp = ByteUtil.hexStr2bytes(recvMessage);
            byte[] bRecvMessage = new byte[iRecvMessageLen];
            System.arraycopy(bTemp, 0, bRecvMessage, 0, iRecvMessageLen);
            return bRecvMessage;
        } catch (IOException e) {
            System.out.println("Recv Message Error:" + e.getMessage());
            return null;
        }
    }


    public boolean isConnect() {
        try {
            if (socket != null && socket.isConnected()) {
                socket.sendUrgentData(0xFF);
                return true;
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public void close() {
        try {
            if (!socket.isInputShutdown()) {
                socket.shutdownInput();
            }
            if (!socket.isOutputShutdown()) {
                socket.shutdownOutput();
            }
            if (socket != null && !socket.isClosed()) {
                socket.close();
                socket = null;
            }
        } catch (Exception e) {
            System.out.println("close socket exption:" + e.getMessage());
        }
    }


    public Socket getSocket() {
        return socket;
    }

    public void setSocket(Socket socket) {
        this.socket = socket;
    }

    /**校验和
     * @param hexdata
     * @return
     */
    public static boolean check2binaryString(String hexdata ) {
        String substr = hexdata.substring(4,hexdata.length()-6);//控制单元中各字节数据(第3～27字节)及应用数据单元数据
        String checkSum = hexdata.substring(hexdata.length()-6,hexdata.length()-4);//校验和(1字节)
        String sum= TransUtils.makeChecksums(substr);//求和
        System.out.println("checkSum:  "+checkSum);
        System.out.println("sum:  "+sum);
        if(sum.equals(checkSum)){
            return true;
        }
        return false;
    }
    /**
     * 应答命令
     */
    public static String answerCommand(String Serial,String commandStr ) {
        //String Serial = hexdata.substring(4,2);
        String str = Serial+  //业务流水号
                "0102"+ //协议版本号
                DateUtil.hexDateString()+ //时间标签
                "50F80B540200"+  //源地址
                "500000000000" + //目的地址
                "0000"   +     //应用数据单元长度
                commandStr;    //命令字节
//        System.out.println("commandStr:  "+commandStr);
        String answerStr ="4040"+str+ TransUtils.makeChecksums(str)+"2323";
        return answerStr;
    }

    class ServerThread extends Thread {

        private Socket client;

        public ServerThread(Socket client) {
            this.client = client;
        }

        @SneakyThrows
        @Override
        public void run() {
            while (true) {
                String recvMessage = "";
                int iMaxLenth = 2048;
                int iRecvMessageLen = 0;
                byte[] bString = new byte[iMaxLenth];
                InputStream is = null;
                try {
                    is = client.getInputStream();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                DataInputStream input = new DataInputStream(is);

                try {
                    int i = input.read(bString);
                    iRecvMessageLen += i;
                    recvMessage += ByteUtil.bytes2hexStr(bString);
                } catch (IOException e) {
                    System.out.println("Recv Message End");
                }
                if (!StringUtils.isNotBlank(recvMessage)) {
                    System.out.println("Recv message is null");
                }
                byte[] bTemp = ByteUtil.hexStr2bytes(recvMessage);
                byte[] bRecvMessage = new byte[iRecvMessageLen];
                System.arraycopy(bTemp, 0, bRecvMessage, 0, iRecvMessageLen);

                byte[] retByte = bRecvMessage;
                String retStr = TransUtils.getBufHexStr(retByte);
                String answerStr = "";
//                System.out.println(retStr);
                if (retStr != null && !retStr.equals("")) {
                    String dataTypeStr= retStr.substring(54,56);//类型标志

                    if("19".equals(dataTypeStr)){
                        System.out.println("-----------------------------心跳--------------------------");
                        String Serial = retStr.substring(4, 8); //流水号
                        byte[] sendInfo = TransUtils.getHexBytes(answerCommand(Serial, FireConstant.KZDYML3));
                        OutputStream os = null;
                        try {
                            os = client.getOutputStream();
                            os.write(sendInfo);
                            os.flush();
                            continue;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }


                    }

                    System.out.println(DateUtil.getDate("yyyy-MM-dd HH:mm:ss") + " ; " + retStr);

                    // 判断命令类型
                    String Serial = retStr.substring(4, 8); //流水号
                    String commandStr = retStr.substring(52, 54);//命令字节
                    System.out.println("commandStrReveive:  " + commandStr);
                    if (!check2binaryString(retStr)) {
                        //对控制命令和发送信息的否认回答
                        answerStr = answerCommand(retStr.substring(4, 8), FireConstant.KZDYML6);
                    } else {
                        switch (commandStr) {
                            case FireConstant.KZDYML1://控制命令
                                answerStr = answerCommand(Serial, FireConstant.KZDYML3);//对控制命令进行确认
                                break;
                            case FireConstant.KZDYML2://发送数据
                                answerStr = answerCommand(Serial, FireConstant.KZDYML3);//对发送数据进行确认
                                break;
                            case FireConstant.KZDYML3://确认
                                break;
                            case FireConstant.KZDYML4://请求
                                answerStr = answerCommand(Serial, FireConstant.KZDYML5);//对请求查询命令进行应答
                                break;
                            case FireConstant.KZDYML5://应答
                                break;
                            case FireConstant.KZDYML6://否认
                                break;
                            default:
                                break;
                        }
                    }
                    byte[] sendInfo = TransUtils.getHexBytes(answerStr);
                    OutputStream os = null;
                    try {
                        os = client.getOutputStream();
                        os.write(sendInfo);
                        os.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }


                    String dataType= retStr.substring(54,56);//类型标志
                    String objectNums= retStr.substring(56,58);//信息对象数目
                    String deviceType= retStr.substring(58,60);//系统类型
                    String systemAddr= retStr.substring(60,62); //系统地址
                    System.out.println("dataType:"+dataType);
                    System.out.println("objectNums:"+objectNums);
                    System.out.println("deviceType:"+deviceType);
                    System.out.println("systemAddr:"+systemAddr);
                    //如果是【建筑消防设施部件运行状态】
                    if(Integer.parseInt(dataType,16)==FireConstant.LXBZ2){
                        String componentType = retStr.substring(62,64); //部件类型
                        String componentAddr = retStr.substring(64,72);//部件地址
                        componentAddr=addressParse(componentAddr);
                        System.out.println("componentType:"+componentType);
                        System.out.println("componentAddr:"+componentAddr);

                        String machineCode= String.format("%09d",Integer.parseInt(componentAddr)).substring(0,3); //获取机器号
                        String loop=String.format("%09d",Integer.parseInt(componentAddr)).substring(3,6);         //回路
                        String deviceAddress=String.format("%09d",Integer.parseInt(componentAddr)).substring(6,9);//设备地址
                        System.out.println("machineCode:"+Integer.valueOf(machineCode)); //机器号
                        System.out.println("loop:"+Integer.valueOf(loop)); //回路
                        System.out.println("deviceAddress:"+Integer.valueOf(deviceAddress)); //设备地址

                        String componentStatus= retStr.substring(72,76);//部件状态
                        System.out.println("componentStatus:"+componentStatus);
                        // 解析部件状态
                        String binaryString=TransUtils.hexString2binaryString(componentStatus.substring(2,4))+""+
                                TransUtils.hexString2binaryString(componentStatus.substring(0,2));
                        System.out.println("binaryString:"+binaryString);

                        // 火警状态 0-无火警 1-有火警
                        if("1".equals(binaryString.charAt(14)+"")){
                            System.out.println("存在火警");
                        }

                        // 故障状态 0-无故障 1-有故障
                        if("1".equals(binaryString.charAt(13)+"")){
                            System.out.println("存在故障");
                        }

                        // 反馈 0-无反馈 1-有反馈
                        if("1".equals(binaryString.charAt(9)+"")){
                            System.out.println("存在反馈");
                        }

                        // 电源 0-无故障 1-有故障
                        if("1".equals(binaryString.charAt(7)+"")){
                            System.out.println("存在电源故障");
                        }
                    }

                    System.out.println("完成数据发送");
                    System.out.println("---------------------------------------------------");
                }
            }
        }
    }

    /** 解析部件地址 **/
    public String addressParse(String componentAddr){
        return String.valueOf(Integer.parseInt(componentAddr.substring(6,8)+""+
                componentAddr.substring(4,6)+""+
                componentAddr.substring(2,4)+""+
                componentAddr.substring(0,2),16));
    }
}
